import gtk, math, Point, getopt, EntryDialog

class Shape:
	
	def __init__(self, window, spec):
		__shapes = []	# a list of shapes (composite pattern)
		self.window = window
		self.gc = window.new_gc()
		self.set_up_gc(spec)
		self.finished = False
		self.all_points = [] 	# fill during the first time drawing
		self.filled= False

	def set_up_gc(self, spec):
		self.trace = 0 if spec.trace == "Normal" else 1 if spec.trace == "Dotted" else 2
		(red,green,blue) = (65535*x for x in spec.color)
		self.double = spec.double
		self.gc.set_rgb_fg_color(gtk.gdk.Color(red,green,blue))

	def translate(self, dx, dy):
		for s in self.shapes:
			for p in s.all_shapes[:]:
				p.x = p.x + dx
				p.y = p.y + dy

	def rotate(self):
		raise Exception("Not implemented yet")
		
	
	def reflect(self):
		raise Exception("Not implemented yet")

	def fill(self):
		raise Exception("Not implemented yet")

	def group(self):
		raise Exception("Not implemented yet")
	
	def ungroup(self):
		raise Exception("Not implemented yet")
		
	def print_spec(self, arq):
		arq.write(str(self.spec))
		

class Line(Shape):
	
	def __init__(self, point1, point2, window, spec):
		Shape.__init__(self, window, spec)
		self.point1 = point1
		self.point2 = point2
		self.spec = (spec, self.point1, self.point2)
	
	def __prepare_to_draw(self):
		steep = abs(self.point1.y-self.point2.y)>abs(self.point1.x-self.point2.x)
		#swap the points		
		if steep:
			self.point1.y,self.point1.x,self.point2.y,self.point2.x = self.point1.x,self.point1.y,self.point2.x,self.point2.y
	
		if self.point1.x>self.point2.x:
			self.point1,self.point2 = self.point2,self.point1
		deltax = self.point2.x-self.point1.x
		deltay = abs(self.point2.y-self.point1.y)
		return (steep,deltax,deltay,deltax//2,self.point1.y,1 if self.point1.y<self.point2.y else -1)
	
	def draw(self):
		if len(self.all_points)==0:
			(steep,deltax,deltay,erro,y,ystep)=self.__prepare_to_draw()
			for x in range(self.point1.x,self.point2.x+1):
				self.all_points.append((y,x)) if steep else self.all_points.append((x,y))
				erro-=deltay
				if erro<0:
					y+=ystep
					erro+=deltax 
			if self.trace == 1:
				self.all_points = [x for x in self.all_points if (x[steep])%2==0]
			elif self.trace == 2:
				self.all_points = [x for x in self.all_points if (x[steep])%6<4]
			if self.double:
				if steep:
					self.all_points = self.all_points + [ (p[0]+1,p[1]) for p in self.all_points ]
				else:
					self.all_points = self.all_points + [ (p[0],p[1]+1) for p in self.all_points ]
			self.all_points = tuple(self.all_points)
		self.window.draw_points(self.gc,self.all_points)		
		self.finished = True
		return self.finished
	def fill(self,gc):
		pass

class Circle(Shape):

	def __init__(self, center, point, window, spec, save):
		Shape.__init__(self, window, spec)
		self.center = center
		self.init_point = point
		self.angle = self.get_angle(save)*math.pi/180;
		self.spec = (spec, self.center.x, self.center.y, self.init_point.x, self.init_point.y, self.angle)
		self.save = save
		self.inside_points=[]
		

	def get_angle(self, save):
		if save:
			win = EntryDialog.EntryDialog("Angulo (em graus): ", "360", True)
			win.show()
			gtk.main()
			angle = int(win.ret)
		else:
			angle = 360
		return angle
		
	
	def draw(self):
		if not self.finished:
			#Preparing to draw
			radius = (self.center-self.init_point).norm()
			if radius < 0.01:
				return
			sin, cos = math.sin(1/radius), math.cos(1/radius)
			angles = [x/radius for x in range(math.trunc(self.angle*radius))]
			lastx = self.init_point.x
			lasty = self.init_point.y
			self.all_points = []
			self.all_points.append((lastx, lasty))
			
			#drawing the normal traced circle
			for a in angles:
				x, y = lastx - self.center.x, lasty - self.center.y
				lastx,  lasty= self.center.x + cos*x + sin*y, self.center.y + cos*y - sin*x
				self.all_points.append((math.trunc(lastx),math.trunc(lasty)))
				if self.double:
					dx, dy = int(abs(x) > abs(y)), int(abs(x) <= abs(y))
					self.all_points.append((math.trunc(lastx + dx), math.trunc(lasty + dy)))
			
			self.all_points = tuple(self.all_points)
			
			if self.save and len(self.inside_points)==0:
				for p in self.all_points:
					if(p[0]<self.center.x):
						for x in range(p[0]+1,2*self.center.x-p[0]):			
							self.inside_points.append((x,p[1]))
	
			#consertar tracejado
			if(self.trace != 0):
				aux = []
				for x in self.all_points:
					index = int(abs(x[0] - self.center.x) > abs(x[1] - self.center.y))
					if (self.trace == 1 and x[index]%2 == 0) or (self.trace==2 and x[index]%7 < 5):
						aux.append((x[0],x[1]))
				self.all_points = tuple(aux)
			#only fill if is saving
			self.finished = True
			
				
		if  self.finished and self.filled and self.angle == 2*math.pi:
			self.window.draw_points(self.fill_gc, self.inside_points)		
		self.window.draw_points(self.gc, self.all_points)
		
		return self.finished
		
	def fill(self,gc):
		if not self.filled:
			self.filled = True
			self.fill_gc = gc
			if  self.finished:
				self.window.draw_points(self.fill_gc, self.inside_points)		
			self.window.draw_points(self.gc, self.all_points)
		

##FIXME The draw method isn's perfect
class Polygon(Shape):

	def __init__(self, first_point, second_point, window, spec):
		Shape.__init__(self, window, spec)
		self.lines = []
		self.points = [first_point, second_point]
		
		print(first_point.x, first_point.y)
		
		self.spec = spec
		self.save = True
	
	def remove_last(self, remove):
		if(remove):
			self.points.pop(-1)
			self.lines.pop(-1)
	
	def check_last_point(self, point):
		if (point - self.points[0]).norm() < 20:
			print "finish"
			self.finished = True
			# create new instance?
			return self.points[0]
		else:
			return point
	
	def add_point(self, point, save):
		self.remove_last(not self.save)
		self.save = save
		if save:
			point = self.check_last_point(point)
			print(point.x, point.y)
		#needed because line change the order of the points 
		point1 = Point.Point(self.points[-1].x, self.points[-1].y)
		point2 = Point.Point(point.x, point.y)
		self.lines.append(Line(point2, point1, self.window, self.spec))
		self.points.append(point)
		
		#better place?
		if self.finished:
			self.spec = (self.spec, self.points)
	
	def draw(self):			
		for line in self.lines:
			line.draw()
		return self.finished
	
	def translate(self, dx, dy):
		for line in self.lines:
			line.traslate(dx,dy)
